很多時候,使用者在刪除某筆資料時,伺服端不會馬上刪除資料,而是保留此資料一段時間,這樣假如使用者反悔的話,還有機會救回來。
Django 的實作中,我們可以在每個想要如此定義的 Model 寫定相關邏輯與操作,如下:
from django.db import models
class ExampleModel(models.Model):
name = models.CharField(null=False)
is_deleted = models.BooleanField(default=False)
def delete(self, soft=True):
if soft:
self.is_deleted = True
self.save()
else:
super().delete()
delete
為內建的方法,這裡我們自己寫了 delete
覆寫了它,從中增加了一些判斷,讓軟刪除得以像一般刪除一樣執行。
假如我們想要讓眾多模型都可以軟刪除,又不想重複寫得話,可以使用 Abstract Models
來設計:
from django.db import models
class SoftDeleteModel(models.Model):
is_deleted = models.BooleanField(default=False)
def delete(self, soft=True):
if soft:
self.is_deleted = True
self.save()
else:
super().delete()
class Meta:
abstract = True
class ExampleModel(SoftDeleteModel):
name = models.CharField(null=False)
再來,為了開發人員寫程式時不用多花心思設立許多 if else
判斷(或是 ORM 的 filter
),可以自定義 models.Manager
在每個流程中暗自加上 filter
效果。使得軟刪除的資料在預設下是拿不到的。
from django.db import models
# 增加自訂義 manager
class SoftDeleteManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_deleted=True)
class SoftDeleteModel(models.Model):
is_deleted = models.BooleanField(default=False)
# 蓋過預設的 objects manager
objects = SoftDeleteManager()
def delete(self, soft=True):
if soft:
self.is_deleted = True
self.save()
else:
super().delete()
class Meta:
abstract = True
class ExampleModel(SoftDeleteModel):
name = models.CharField(null=False)
ExampleModel.objects.all()
# 只會拿到還沒被軟刪除的資料,即使寫法跟預設一樣
設定可以很多,也可以寫讓軟刪除復原的方法,程式上都行,主要考量還是業務邏輯。以下是我自己使用的模板:
from django.db import models
from django.utils import timezone
class SoftDeleteManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(deleted_at=None)
class DeletedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().exclude(deleted_at=None)
class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(default=None, null=True)
objects = SoftDeleteManager()
deleted_objects = DeletedManager()
all_objects = models.Manager()
def delete(self, soft=True):
if soft:
self.deleted_at = timezone.now()
self.save()
else:
super().delete()
def revive(self):
self.deleted_at = None
self.save()
def is_deleted(self):
return self.deleted_at is not None
class Meta:
abstract = True